﻿
using System;
using System.Collections.Generic;
using System.Linq;
using System.Linq.Expressions;
using System.Text;
using System.Workflow.ComponentModel;
using System.Workflow.Activities;
using System.Diagnostics;
using System.Xml;
using System.Reflection;
using System.IO;
using System.Collections;

using Antlr.Runtime.Tree;
using Antlr.Runtime;

using TextFlow.Activities;

namespace TextFlow.Engine
{
	public class TextFlowCompiler
	{
        public static TextFlowWorkflow Compile( string languageText )
        {
            if ( string.IsNullOrEmpty( languageText ) )
            {
                throw new ArgumentNullException( "languageText" );
            }

            TextFlowLangLexer lexer = new TextFlowLangLexer( new ANTLRStringStream( languageText ) );

            CommonTokenStream tokens = new CommonTokenStream( lexer );

            TextFlowLangParser parser = new TextFlowLangParser( tokens );

            Stack stack = new Stack();

            SymbolTable varMap = new SymbolTable();

            ActivityFactory.Init();

            TextFlowWorkflow workflow = new TextFlowWorkflow();

            workflow.Name = "MainFlow";

            parser.TxnStart += delegate( object sender, TextFlowEventArgs e )
            {
                bool usesDTC = e.Value<bool>( 0 );

                Activity txn = null;

                if ( usesDTC )
                {
                    CompensatableTransactionScopeActivity ctsa = ActivityFactory.Create<CompensatableTransactionScopeActivity>();

                    WorkflowTransactionOptions options = new WorkflowTransactionOptions();

                    options.IsolationLevel = System.Transactions.IsolationLevel.Serializable;
                    options.TimeoutDuration = TimeSpan.FromSeconds( 30 );

                    ctsa.TransactionOptions = options;

                    txn = ctsa;
                }
                else
                {
                    txn = ActivityFactory.Create<CompensatableSequenceActivity>();
                }

                Debug.Assert( stack.Count > 0 );
                Debug.Assert( stack.Peek() is CompositeActivity );

                CompositeActivity composite = stack.Peek() as CompositeActivity;

                Debug.Assert( composite != null );

                composite.Activities.Add( txn );

                stack.Push( txn );
            };

            parser.TxnEnd += delegate( object sender, TextFlowEventArgs e )
            {
                Debug.Assert( stack.Count >= 1 );
                Debug.Assert( stack.Peek() is ICompensatableActivity );

                stack.Pop();
            };

            parser.RollbackStart += delegate( object sender, TextFlowEventArgs e )
            {
                CompensationHandlerActivity handler = ActivityFactory.Create<CompensationHandlerActivity>();

                Debug.Assert( stack.Count > 0 );
                Debug.Assert( stack.Peek() is CompensatableSequenceActivity || stack.Peek() is CompensatableTransactionScopeActivity );

                CompositeActivity composite = stack.Peek() as CompositeActivity;

                Debug.Assert( composite != null );
                Debug.Assert( composite.Activities.Exists( a => a is CompensationHandlerActivity ) == false );

                composite.Activities.Add( handler );

                stack.Push( handler );
            };

            parser.RollbackEnd += delegate( object sender, TextFlowEventArgs e )
            {
                Debug.Assert( stack.Count >= 1 );
                Debug.Assert( stack.Peek() is CompensationHandlerActivity );

                stack.Pop();
            };

            parser.OnErrorStart += delegate( object sender, TextFlowEventArgs e )
            {
                Debug.Assert( stack.Count > 0 );
                Debug.Assert( stack.Peek() is CompositeActivity );

                CompositeActivity composite = stack.Peek() as CompositeActivity;

                Debug.Assert( composite != null );

                FaultHandlersActivity handlers = null;

                if ( composite.Activities.Exists( a => a is FaultHandlersActivity ) )
                {
                    handlers = composite.Activities.OfType<FaultHandlersActivity>().First();
                }
                else
                {
                    handlers = ActivityFactory.Create<FaultHandlersActivity>();

                    composite.Activities.Add( handlers );
                }

                FaultHandlerActivity handler = ActivityFactory.Create<FaultHandlerActivity>();

                handler.FaultType = typeof( Exception );    // handle all exception types...

                handlers.Activities.Add( handler );

                stack.Push( handler );
            };

            parser.OnErrorEnd += delegate( object sender, TextFlowEventArgs e )
            {
                Debug.Assert( stack.Count >= 1 );
                Debug.Assert( stack.Peek() is FaultHandlerActivity );

                stack.Pop();
            };

            parser.Stop += delegate( object sender, TextFlowEventArgs e )
            {
                SuspendActivity suspend = ActivityFactory.Create<SuspendActivity>();

                suspend.Error = string.Format( "__internalsuspend:{0}", suspend.Name );

                Debug.Assert( stack.Count > 0 );
                Debug.Assert( stack.Peek() is CompositeActivity );

                CompositeActivity composite = stack.Peek() as CompositeActivity;

                Debug.Assert( composite != null );

                composite.Activities.Add( suspend );
            };

            parser.Error += delegate( object sender, TextFlowEventArgs e )
            {
                string message = e.Value<string>( 0 );

                ThrowActivity error = ActivityFactory.Create<ThrowActivity>();

                error.Fault = new TextFlowException( message );

                Debug.Assert( stack.Count > 0 );
                Debug.Assert( stack.Peek() is CompositeActivity );

                CompositeActivity composite = stack.Peek() as CompositeActivity;

                Debug.Assert( composite != null );

                composite.Activities.Add( error );
            };

            parser.Delay += delegate( object sender, TextFlowEventArgs e )
            {
                string time = e.Value<string>( 0 );
                string unit = e.Value<string>( 1 );

                DelayActivity delay = ActivityFactory.Create<DelayActivity>();

                switch ( unit )
                {
                    case "s":
                        delay.TimeoutDuration = TimeSpan.FromSeconds( Convert.ToDouble( time ) );
                        break;

                    case "m":
                        delay.TimeoutDuration = TimeSpan.FromMinutes( Convert.ToDouble( time ) );
                        break;
                }

                Debug.Assert( stack.Count > 0 );
                Debug.Assert( stack.Peek() is CompositeActivity );

                CompositeActivity composite = stack.Peek() as CompositeActivity;

                Debug.Assert( composite != null );

                composite.Activities.Add( delay );
            };

            parser.Lookup += delegate( object sender, TextFlowEventArgs e )
            {
                string val = e.Value<string>( 0 );

                Type t = varMap[ val ];

                Expression expr = Expression.Call( Expression.Parameter( typeof( TextFlowWorkflow ), "root" ), "Lookup", new Type[] { t }, Expression.Constant( val ) );

                stack.Push( expr );
            };

            parser.Literal += delegate( object sender, TextFlowEventArgs e )
            {
                Expression expr = null;

                if ( e.ParamCount == 0 )
                {
                    expr = Expression.Constant( null );
                }
                else
                {
                    Debug.Assert( e.ParamCount == 2 );

                    Type type = e.Value<Type>( 0 );
                    string val = e.Value<string>( 1 );

                    switch ( type.Name )
                    {
                        case "Boolean":
                            expr = Expression.Constant( bool.Parse( val ) );
                            break;

                        case "String":
                            expr = Expression.Constant( val.Trim( '"' ) );
                            break;

                        case "Decimal":
                            expr = Expression.Constant( decimal.Parse( val.TrimEnd( 'd' ) ) );
                            break;

                        case "Double":
                            expr = Expression.Constant( double.Parse( val.TrimEnd( 'f' ) ) );
                            break;

                        case "Char":
                            expr = Expression.Constant( char.Parse( val.Trim( '\'' ) ) );
                            break;
                    }
                }

                Debug.Assert( expr != null );

                stack.Push( expr );
            };

            parser.Math += delegate( object sender, TextFlowEventArgs e )
            {
                string op = e.Value<string>( 0 );

                Expression rhs = stack.Pop() as Expression;
                Expression lhs = null;

                if ( op != "Negate" )
                {
                    lhs = stack.Pop() as Expression;
                }

                Expression expr = null;

                switch ( op )
                {
                    case "+":
                        expr = Expression.AddChecked( lhs, rhs );
                        break;

                    case "-":
                        expr = Expression.SubtractChecked( lhs, rhs );
                        break;

                    case "*":
                        expr = Expression.MultiplyChecked( lhs, rhs );
                        break;

                    case "/":
                        expr = Expression.Divide( lhs, rhs );
                        break;

                    case "Negate":
                        expr = Expression.NegateChecked( rhs );
                        break;
                }

                Debug.Assert( expr != null );

                stack.Push( expr );
            };

            parser.Compare += delegate( object sender, TextFlowEventArgs e )
            {
                string op = e.Value<string>( 0 );

                Expression rhs = stack.Pop() as Expression;
                Expression lhs = null;

                if ( op != "NOT" )
                {
                    lhs = stack.Pop() as Expression;
                }

                Expression expr = null;

                switch ( op )
                {
                    case "==":
                        expr = Expression.Equal( lhs, rhs );
                        break;

                    case "!=":
                        expr = Expression.NotEqual( lhs, rhs );
                        break;

                    case ">":
                        expr = Expression.GreaterThan( lhs, rhs );
                        break;

                    case ">=":
                        expr = Expression.GreaterThanOrEqual( lhs, rhs );
                        break;

                    case "<":
                        expr = Expression.LessThan( lhs, rhs );
                        break;

                    case "<=":
                        expr = Expression.LessThanOrEqual( lhs, rhs );
                        break;

                    case "AND":
                        expr = Expression.And( lhs, rhs );
                        break;

                    case "OR":
                        expr = Expression.Or( lhs, rhs );
                        break;

                    case "NOT":
                        expr = Expression.Not( rhs );
                        break;
                }

                Debug.Assert( expr != null );

                stack.Push( expr );
            };

            parser.Method += delegate( object sender, TextFlowEventArgs e )
            {
                string service = e.Value<string>( 0 );
                string method = e.Value<string>( 1 );
                int parmCount = e.Value<int>( 2 );

                Stack<Expression> parms = new Stack<Expression>();

                while ( parmCount > 0 )
                {
                    parms.Push( stack.Pop() as Expression );

                    parmCount--;
                }

                Expression call = FunctionGenerator.GenerateMethodCall( service, method, parms );

                Debug.Assert( call != null );

                stack.Push( call );
            };

            parser.Property += delegate( object sender, TextFlowEventArgs e )
            {
                string service = e.Value<string>( 0 );
                string prop = e.Value<string>( 1 );

                Expression call = FunctionGenerator.GeneratePropertyLookup( service, prop );

                Debug.Assert( call != null );

                stack.Push( call );
            };

            parser.ActionInput += delegate( object sender, TextFlowEventArgs e )
            {
                Debug.Assert( stack.Count >= 2 );
                Debug.Assert( stack.Peek() is Expression );

                string boundPropertyName = e.Value<string>( 0 );
                Expression expr = stack.Pop() as Expression;

                ActionInput input = new ActionInput();

                input.BoundPropertyName = boundPropertyName;
                input.Expr = expr;

                stack.Push( input );
            };

            parser.Action += delegate( object sender, TextFlowEventArgs e )
            {
                Activity action = ActivityFactory.Create( e.Value<string>( 0 ) );

                Debug.Assert( action != null );

                Stack<ActionInputActivity> inputs = new Stack<ActionInputActivity>();

                while ( stack.Peek() is ActionInput )
                {
                    ActionInput input = stack.Pop() as ActionInput;

                    DependencyProperty prop = DependencyProperty.FromName( input.BoundPropertyName, action.GetType() );

                    if ( prop == null )
                    {
                        throw new InvalidOperationException( string.Format( "Cannot resolve DependencyProperty {0} for [{1}].", input.BoundPropertyName, e.Value<string>( 0 ) ) );
                    }

                    if ( prop.PropertyType.IsAssignableFrom( input.Expr.Type ) == false )
                    {
                        MethodInfo generic = typeof( Converter ).GetMethod( "Convert" );

                        Debug.Assert( generic != null );

                        MethodInfo concrete = generic.MakeGenericMethod( prop.PropertyType, input.Expr.Type );

                        Debug.Assert( concrete != null );

                        input.Expr = Expression.ConvertChecked( input.Expr, prop.PropertyType, concrete );
                    }

                    ActionInputActivity activity = ActivityFactory.CreateInputActivity( input.BoundPropertyName, input.Expr.Type );

                    Debug.Assert( activity != null );

                    activity.BoundPropertyName = input.BoundPropertyName;
                    activity.LHS = input.Expr;

                    ActivityBind bind = new ActivityBind();

                    bind.Name = activity.Name;
                    bind.Path = activity.BoundPropertyName;

                    action.SetBinding( prop, bind );

                    inputs.Push( activity );
                }

                ActionSequenceActivity actionSequence = ActivityFactory.Create<ActionSequenceActivity>();

                actionSequence.ConfiguredAction = e.Value<string>( 0 );

                while ( inputs.Count > 0 )
                {
                    actionSequence.Activities.Add( inputs.Pop() );
                }

                actionSequence.Activities.Add( action );

                CompositeActivity composite = stack.Peek() as CompositeActivity;

                Debug.Assert( composite != null );

                composite.Activities.Add( actionSequence );

                stack.Push( actionSequence );
                stack.Push( action );
            };

            parser.ActionOutput += delegate( object sender, TextFlowEventArgs e )
            {
                string boundPropertyName = e.Value<string>( 0 );
                string variableName = e.Value<string>( 1 );

                Debug.Assert( stack.Count >= 2 );
                Debug.Assert( stack.Peek() is Activity );

                Activity action = stack.Pop() as Activity;

                PropertyInfo prop = action.GetType().GetProperty( boundPropertyName );

                Debug.Assert( prop != null );

                varMap.Add( variableName, prop.PropertyType );

                ActionOutputActivity output = ActivityFactory.CreateOutputActivity( boundPropertyName, prop.PropertyType );

                Debug.Assert( output != null );

                output.BoundPropertyName = boundPropertyName;
                output.VariableName = variableName;

                ActivityBind bind = new ActivityBind();

                bind.Name = action.Name;
                bind.Path = boundPropertyName;

                DependencyProperty dp = DependencyProperty.FromName( boundPropertyName, output.GetType() );

                if ( dp == null )
                {
                    throw new InvalidOperationException( string.Format( "Cannot resolve DependencyProperty {0} for output action activity.", boundPropertyName ) );
                }

                output.SetBinding( dp, bind );

                Debug.Assert( stack.Peek() is ActionSequenceActivity );

                ActionSequenceActivity actionSequence = stack.Peek() as ActionSequenceActivity;

                actionSequence.Activities.Add( output );

                stack.Push( action );
            };

            parser.PostAction += delegate( object sender, TextFlowEventArgs e )
            {
                Debug.Assert( stack.Count >= 2 );
                Debug.Assert( stack.Peek() is Activity );

                stack.Pop();

                Debug.Assert( stack.Peek() is ActionSequenceActivity );

                stack.Pop();
            };

            parser.Condition += delegate( object sender, TextFlowEventArgs e )
            {
                TextFlowCondition condition = new TextFlowCondition();

                Debug.Assert( stack.Count >= 2 );

                Debug.Assert( stack.Peek() is Expression );

                condition.EvalExpr = stack.Pop() as Expression;

                if ( stack.Peek() is IfElseBranchActivity )
                {
                    IfElseBranchActivity branch = stack.Peek() as IfElseBranchActivity;

                    Debug.Assert( branch != null );
                    Debug.Assert( branch.Condition == null );

                    branch.Condition = condition;
                }
                else if ( stack.Peek() is SequenceActivity )
                {
                    SequenceActivity sa = stack.Pop() as SequenceActivity;

                    Debug.Assert( stack.Peek() is TextFlowWhileActivity );

                    TextFlowWhileActivity wa = stack.Peek() as TextFlowWhileActivity;

                    Debug.Assert( wa != null );
                    Debug.Assert( wa.Condition == null );

                    wa.Condition = condition;

                    stack.Push( sa );
                }
                else
                {
                    throw new InvalidOperationException( "Unexpected next activity found: " + stack.Peek().GetType().Name );
                }
            };

            parser.Assign += delegate( object sender, TextFlowEventArgs e )
            {
                StoreVariableActivity storevar = ActivityFactory.Create<StoreVariableActivity>();

                storevar.LHS = e.Value<string>( 0 );

                Debug.Assert( stack.Count >= 2 );

                Debug.Assert( stack.Peek() is Expression );

                storevar.RHS = stack.Pop() as Expression;

                Debug.Assert( stack.Peek() is CompositeActivity );

                CompositeActivity composite = stack.Peek() as CompositeActivity;

                Debug.Assert( composite != null );

                composite.Activities.Add( storevar );

                varMap.Add( storevar.LHS, storevar.RHS.Type );
            };

            parser.WhileStart += delegate( object sender, TextFlowEventArgs e )
            {
                TextFlowWhileActivity wa = ActivityFactory.Create<TextFlowWhileActivity>();

                SequenceActivity sa = ActivityFactory.Create<SequenceActivity>();

                wa.Activities.Add( sa );

                Debug.Assert( stack.Count > 0 );
                Debug.Assert( stack.Peek() is CompositeActivity );

                CompositeActivity composite = stack.Peek() as CompositeActivity;

                Debug.Assert( composite != null );

                composite.Activities.Add( wa );

                stack.Push( wa );
                stack.Push( sa );
            };

            parser.WhileEnd += delegate( object sender, TextFlowEventArgs e )
            {
                Debug.Assert( stack.Count >= 2 );

                Debug.Assert( stack.Peek() is SequenceActivity );
                stack.Pop();

                Debug.Assert( stack.Peek() is TextFlowWhileActivity );
                stack.Pop();
            };

            parser.IterationStart += delegate( object sender, TextFlowEventArgs e )
            {
                int iterations = int.Parse( e.Value<string>( 0 ) );

                TextFlowIteratorActivity it = ActivityFactory.Create<TextFlowIteratorActivity>();

                it.Iterations = iterations;

                SequenceActivity sa = ActivityFactory.Create<SequenceActivity>();

                it.Activities.Add( sa );

                Debug.Assert( stack.Count > 0 );
                Debug.Assert( stack.Peek() is CompositeActivity );

                CompositeActivity composite = stack.Peek() as CompositeActivity;

                Debug.Assert( composite != null );

                composite.Activities.Add( it );

                stack.Push( it );
                stack.Push( sa );
            };

            parser.IterationEnd += delegate( object sender, TextFlowEventArgs e )
            {
                Debug.Assert( stack.Count >= 1 );

                Debug.Assert( stack.Peek() is SequenceActivity );
                stack.Pop();

                Debug.Assert( stack.Peek() is TextFlowIteratorActivity );
                stack.Pop();
            };

            parser.ParallelStart += delegate( object sender, TextFlowEventArgs e )
            {
                ParallelActivity pa = ActivityFactory.Create<ParallelActivity>();

                Debug.Assert( stack.Count > 0 );
                Debug.Assert( stack.Peek() is CompositeActivity );

                CompositeActivity composite = stack.Peek() as CompositeActivity;

                Debug.Assert( composite != null );

                composite.Activities.Add( pa );

                stack.Push( pa );
            };

            parser.ParallelChildStart += delegate( object sender, TextFlowEventArgs e )
            {
                SequenceActivity sequence = ActivityFactory.Create<SequenceActivity>();

                Debug.Assert( stack.Count > 0 );
                Debug.Assert( stack.Peek() is ParallelActivity );

                ParallelActivity parallel = stack.Peek() as ParallelActivity;

                Debug.Assert( parallel != null );

                parallel.Activities.Add( sequence );

                stack.Push( sequence );
            };

            parser.ParallelChildEnd += delegate( object sender, TextFlowEventArgs e )
            {
                Debug.Assert( stack.Count >= 1 );

                Debug.Assert( stack.Peek() is SequenceActivity );

                stack.Pop();
            };

            parser.ParallelEnd += delegate( object sender, TextFlowEventArgs e )
            {
                Debug.Assert( stack.Count >= 1 );

                Debug.Assert( stack.Peek() is ParallelActivity );

                stack.Pop();
            };

            parser.IfStart += delegate( object sender, TextFlowEventArgs e )
            {
                IfElseActivity ifelse = ActivityFactory.Create<IfElseActivity>();

                Debug.Assert( stack.Count > 0 );
                Debug.Assert( stack.Peek() is CompositeActivity );

                CompositeActivity composite = stack.Peek() as CompositeActivity;

                Debug.Assert( composite != null );

                composite.Activities.Add( ifelse );

                stack.Push( ifelse );

                IfElseBranchActivity branch = ActivityFactory.Create<IfElseBranchActivity>();

                ifelse.Activities.Add( branch );

                stack.Push( branch );
            };

            parser.ElseIfClause += delegate( object sender, TextFlowEventArgs e )
            {
                Debug.Assert( stack.Count > 1 );
                Debug.Assert( stack.Peek() is IfElseBranchActivity );

                stack.Pop();

                Debug.Assert( stack.Peek() is IfElseActivity );

                IfElseActivity ifelse = stack.Peek() as IfElseActivity;

                IfElseBranchActivity branch = ActivityFactory.Create<IfElseBranchActivity>();

                ifelse.Activities.Add( branch );

                stack.Push( branch );
            };

            parser.ElseClause += delegate( object sender, TextFlowEventArgs e )
            {
                Debug.Assert( stack.Count > 1 );
                Debug.Assert( stack.Peek() is IfElseBranchActivity );

                stack.Pop();

                Debug.Assert( stack.Peek() is IfElseActivity );

                IfElseActivity ifelse = stack.Peek() as IfElseActivity;

                IfElseBranchActivity branch = ActivityFactory.Create<IfElseBranchActivity>();

                ifelse.Activities.Add( branch );

                stack.Push( branch );
            };

            parser.IfEnd += delegate( object sender, TextFlowEventArgs e )
            {
                Debug.Assert( stack.Count >= 2 );

                Debug.Assert( stack.Peek() is IfElseBranchActivity );

                stack.Pop();

                Debug.Assert( stack.Peek() is IfElseActivity );

                stack.Pop();
            };

            parser.ProgStart += delegate( object sender, TextFlowEventArgs e )
            {
                stack.Push( workflow );
            };

            parser.ProgEnd += delegate( object sender, TextFlowEventArgs e )
            {
                Debug.Assert( stack.Count == 1 );
                Debug.Assert( stack.Peek() is TextFlowWorkflow );

                stack.Pop();
            };

            parser.prog();

            if ( parser.HasErrors )
            {
                throw new TextFlowCompilationException( parser.Errors );
            }
            else
            {
                Debug.Assert( stack.Count == 0 );

                return workflow;
            }
        }
	}

    public class TextFlowEventArgs : EventArgs
    {
        private List<object> _parms;

        public TextFlowEventArgs( params object[] parms )
        {
            _parms = new List<object>( parms );
        }

        public int ParamCount
        {
            get { return _parms.Count; }
        }

        public T Value<T>( int i )
        {
            if ( _parms.Count == 0 )
            {
                return default( T );
            }
            else if ( i < 0 || i >= _parms.Count )
            {
                return default( T );
            }
            else
            {
                return (T) PostProcess( _parms[ i ], typeof( T ) );
            }
        }

        private object PostProcess( object o, Type t )
        {
            if ( t == typeof( string ) )
            {
                return ( o as string ).Trim( '"', '\'' );
            }
            else
            {
                return o;
            }
        }
    }
}
